package entity;

import org.lwjgl.util.vector.Vector3f;

import entity.characters.Character;

/**
 * 
 * @author Jevgeni Krutov
 *
 */
public class MovingEntity extends Entity {
	private static final float MAX_X_ROTATION = 89;
	private static final float MIN_X_ROTATION = -89;
	
	private static final float GROUND_RESISTANCE_FACTOR = 1000;
	private static final float AIR_RESISTANCE_FACTOR = 700;
	
	protected int currentW;
	protected Vector3f forcesSum = new Vector3f(0, 0, 0);
	protected Vector3f speed;
	
	private float mass;
	
	public MovingEntity(Vector3f position, Vector3f rotation) {
		super(position, rotation);
		speed = new Vector3f(0,0,0);
		mass = 1f;
	}
	
	public Vector3f getNormalisedForwardVector() {
		Vector3f v = new Vector3f(
				-(float) (Math.sin(Math.toRadians(rotation.y))),
				(float) (Math.tan(Math.toRadians(rotation.x))),
				-(float) (Math.cos(Math.toRadians(rotation.y))));
		v.normalise();
		return v;
	}
	
	
	// common methods below
	
	@Override
	public void addPosition(Vector3f position) {
		addPosition(position.x, position.y, position.z);
		invalidateMatrix();
	}
	
	@Override
	public void addPosition(float x, float y, float z) {
		position.x += x;
		position.y += y;
		position.z += z;
		invalidateMatrix();
	}
	
	@Override
	public void addRotation(Vector3f rotation) {
		addRotation(rotation.x, rotation.y, rotation.z);
		invalidateMatrix();
	}
	
	@Override
	public void addRotation(float rx, float ry, float rz) {
		float predictSum = rotation.x + rx;
		if(predictSum > MIN_X_ROTATION && predictSum < MAX_X_ROTATION) {
			rotation.x += rx;
		} else {
			if(predictSum > 0) {
				rotation.x = MAX_X_ROTATION;
			} else {
				rotation.x = MIN_X_ROTATION;
			}
		}
		rotation.y = (rotation.y + ry) % 360f; //modulus
		rotation.z = rotation.z + rz;
		
		invalidateMatrix();
	}
	
	@Override
	protected void moveInDirection(float amount, float direction) {
		position.z += amount * Math.cos(Math.toRadians(rotation.y + 90 * direction));
		position.x += amount * Math.sin(Math.toRadians(rotation.y + 90 * direction));
	
		if(direction != -1 /*strafe*/) { //no altitude change in case of strafe
			position.y += amount * (Math.cos(Math.toRadians(rotation.x + 90)) + Math.cos(Math.toRadians(rotation.z + 90)));
		}
		invalidateMatrix();
	}
	
	@Override
	public void applyForce(Vector3f force) {
		Vector3f.add(forcesSum, force, forcesSum);
	}
	
	@Override
	public void changeSpeedAndMove(long elapsedTime) {
		float speedVectorLength = speed.length();
		if(speedVectorLength > 0) {
			Vector3f airResistance = new Vector3f(speed);
			airResistance.negate();
			airResistance.normalise();
			
			Vector3f groundResistance = new Vector3f(airResistance);
			
			if(this.isCharacter()) {
				Character ch = (Character) this;
				if(ch.isGrounded()) {
					groundResistance.scale(GROUND_RESISTANCE_FACTOR * speedVectorLength);
					applyForce(groundResistance);
				}
			}
			
			airResistance.scale(AIR_RESISTANCE_FACTOR * speed.lengthSquared());
			applyForce(airResistance);
		}
		
		//plus delta speed
		Vector3f deltaSpeed = new Vector3f(forcesSum);
		deltaSpeed.scale(0.01f / mass);
		Vector3f.add(speed, deltaSpeed, speed);		
		
		forcesSum.set(0, 0, 0);
		addPosition(speed);
//		if(Math.abs(speed.x) < 0.001) {
//			speed.x = 0;
//		}
//		if(Math.abs(speed.y) < 0.001) {
//			speed.y = 0;
//		}
//		if(Math.abs(speed.z) < 0.001) {
//			speed.z = 0;
//		}		
	}
	
	@Override
	public boolean isMovingEntity() {
		return true;
	}

	
	//basic getters and setters below
	
	public int getCurrentW() {
		return currentW;
	}
	
	@Override
	public Vector3f getSpeed() {
		return speed;
	}
	@Override
	public void setSpeed(Vector3f speed) {
		this.speed = speed;
	}
	@Override
	public float getMass() {
		return mass;
	}
	@Override
	public void setMass(float mass) {
		this.mass = mass;
	}
}
